package co.gov.medellin.ssti.sub.model.dao.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.stereotype.Repository;

import co.gov.medellin.ssti.sub.common.util.FiltrosReportesUtil;
import co.gov.medellin.ssti.sub.model.dao.ReporteTotalComunaDAO;
import co.gov.medellin.ssti.sub.model.service.ReporteTotalComunaService;
import co.gov.medellin.ssti.sub.model.service.VflexService;
import co.gov.medellin.ssti.sub.model.vo.CondicionConsultaVO;
import co.gov.medellin.ssti.sub.model.vo.ReportComunaVO;
import co.gov.medellin.ssti.sub.model.vo.ReporteTotalComunaVO;
import co.gov.medellin.ssti.sub.model.vo.VflexVO;

@Repository
public class ReporteTotalComunaDAOImpl2 extends NamedParameterJdbcDaoSupport
		implements ReporteTotalComunaDAO {
	@Autowired
	VflexService vflexServiceImpl;

	private static final String SIN_COMUNA = "SIN COMUNA";

	public Collection<ReportComunaVO> findBeneficiosBeneficiariosComuna(
			ReporteTotalComunaVO criteria,
			Collection<CondicionConsultaVO> filtros) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		// parte adicional de la consulta (criterios de busqueda.)
		StringBuffer queryVars = new StringBuffer();
		StringBuffer sbQueryVars = new StringBuffer();

		// si tiene comuna elegida, solo busca por comuna.
		if ((Object) criteria.getCodeComuna() != null) {
			queryVars.append(" AND comuna.CODIGO = :comuna.CODIGO ");
			sbQueryVars.append(" AND comuna_.CODIGO = :comuna.CODIGO ");
			namedParameters.put("comuna.CODIGO", criteria.getCodeComuna());

		} else {// si no tiene comuna elegida
			// si tiene elegida una zona, pero no tiene elegida una comuna..
			if ((Object) criteria.getCodeZona() != null) {

				queryVars
						.append(" AND (ben.vfl_codigo_zona = :vfl_codigo_zona ");
				sbQueryVars
						.append(" AND (ben_.vfl_codigo_zona = :vfl_codigo_zona ");
				namedParameters.put("vfl_codigo_zona", criteria.getCodeZona());

				Collection<VflexVO> comunas = vflexServiceImpl.findVflex(
						VflexService.COMUNA,
						Long.toString(criteria.getCodeZona().longValue()));
				Iterator<VflexVO> it = comunas.iterator();
				while (it.hasNext()) {
					VflexVO vflexVO = (VflexVO) it.next();
					queryVars
							.append(" OR ben.vfl_codigo_comuna = :vfl_codigo_comuna_"
									+ vflexVO.getCode());
					sbQueryVars
							.append(" OR ben_.vfl_codigo_comuna = :vfl_codigo_comuna_"
									+ vflexVO.getCode());
					namedParameters.put(
							"vfl_codigo_comuna_" + vflexVO.getCode(),
							vflexVO.getCode());
				}

				queryVars.append(" ) ");
				sbQueryVars.append(" ) ");
			}
		}

		if ((Object) criteria.getCodeSubprograma() != null) {
			queryVars.append(" AND subpro.CODIGO = :subpro.CODIGO ");
			sbQueryVars.append(" AND subpro_.CODIGO = :subpro.CODIGO ");
			namedParameters.put("subpro.CODIGO", criteria.getCodeSubprograma());

		}
		if ((Object) criteria.getCodePrograma() != null) {
			queryVars.append(" AND prog.CODIGO = :prog.CODIGO ");
			sbQueryVars.append(" AND prog_.CODIGO = :prog.CODIGO ");
			namedParameters.put("prog.CODIGO", criteria.getCodePrograma());

		}
		if ((Object) criteria.getDesde() != null
				&& (Object) criteria.getHasta() != null) {
			queryVars
					.append(" AND beneotor.FECHA_BENEFICIO BETWEEN TO_DATE(:DESDE,'dd/mm/rrrr') AND TO_DATE(:HASTA,'dd/mm/rrrr') ");
			sbQueryVars
					.append(" AND beneotor_.FECHA_BENEFICIO BETWEEN TO_DATE(:DESDE,'dd/mm/rrrr') AND TO_DATE(:HASTA,'dd/mm/rrrr') ");
			namedParameters.put("DESDE", criteria.getDesde());
			namedParameters.put("HASTA", criteria.getHasta());

		}
		if ((Object) criteria.getDesde() != null
				&& (Object) criteria.getHasta() == null) {
			queryVars
					.append(" AND beneotor.FECHA_BENEFICIO >= TO_DATE(:DESDE,'dd/mm/rrrr') ");
			sbQueryVars
					.append(" AND beneotor_.FECHA_BENEFICIO >= TO_DATE(:DESDE,'dd/mm/rrrr')");
			namedParameters.put("DESDE", criteria.getDesde());

		}
		if ((Object) criteria.getDesde() == null
				&& (Object) criteria.getHasta() != null) {
			queryVars
					.append(" AND beneotor.FECHA_BENEFICIO <= TO_DATE(:HASTA,'dd/mm/rrrr') ");
			sbQueryVars
					.append(" AND beneotor_.FECHA_BENEFICIO <= TO_DATE(:HASTA,'dd/mm/rrrr') ");
			namedParameters.put("HASTA", criteria.getHasta());

		}

		Object[] ret = buildQueryFiltros(filtros);
		String queryFiltVars = (String) ret[0];
		// Map<String, Object> queryFiltParams = (Map<String, Object>) ret[1];
		String sbQueryFiltVars = (String) ret[2];
		Map<String, Object> sbQueryFiltParams = (Map<String, Object>) ret[3];

		namedParameters.putAll(sbQueryFiltParams);

		String query = "SELECT COUNT(DISTINCT beneotor.BEN_CODIGO) contbeneficiario, COUNT(beneotor.CODIGO) contbeneficio, "
				+ " SUM(beneotor.VALOR_BENEFICIO) valor, comuna.NOMBRE nomcomuna, prog.NOMBRE nompro, subpro.NOMBRE nomsubpro "

				// subquery
				+ " , (SELECT COUNT(DISTINCT ben_.codigo) FROM TCS_BEN_AREA subarea_ JOIN TCS_BEN_SUBPROGRAMA subpro_ ON subarea_.codigo = subpro_.area_codigo JOIN TCS_BEN_BENEFICIO_OTORGADO beneotor_ ON beneotor_.sbpr_codigo = subpro_.codigo JOIN TCS_BEN_BENEFICIARIO ben_ ON beneotor_.ben_codigo = ben_.codigo JOIN TCS_BEN_PROGRAMA prog_ ON subpro_.prg_codigo = prog_.codigo JOIN TCS_BEN_AREA area_ ON area_.codigo = subarea_.area_codigo LEFT OUTER JOIN PAOYER.TCS_VALOR_FLEXIBLE comuna_ ON ben_.vfl_codigo_comuna = comuna_.codigo WHERE prog_.vigencia='A' and subpro_.vigencia='A' "
				+ sbQueryVars.toString()
				+ sbQueryFiltVars
				+ " ) AS TOTAL_BENEFICIARIOS "

				+ ", area.NOMBRE nombre_secretaria "
				+ " FROM TCS_BEN_AREA subarea "
				+ " JOIN TCS_BEN_SUBPROGRAMA subpro"
				+ " ON subarea.codigo = subpro.area_codigo "
				+ " JOIN TCS_BEN_BENEFICIO_OTORGADO beneotor "
				+ " ON beneotor.sbpr_codigo = subpro.codigo "
				+ " JOIN TCS_BEN_BENEFICIARIO ben "
				+ " ON beneotor.ben_codigo = ben.codigo "
				+ " JOIN TCS_BEN_PROGRAMA prog "
				+ " ON subpro.prg_codigo = prog.codigo "
				+ " JOIN TCS_BEN_AREA area "
				+ " ON area.codigo = subarea.area_codigo "
				+ " LEFT OUTER JOIN PAOYER.TCS_VALOR_FLEXIBLE comuna "
				+ " ON ben.vfl_codigo_comuna = comuna.codigo "
				+ " WHERE prog.vigencia='A' and subpro.vigencia='A' ";

		query += queryVars.toString();
		query += queryFiltVars;
		if (criteria.getTipoReporte().equals(
				ReporteTotalComunaService.TIPO_REPORTE_1)) {
			query += " GROUP BY area.nombre, comuna.NOMBRE, prog.NOMBRE,subpro.NOMBRE ";
			query += " ORDER BY area.nombre, comuna.NOMBRE, prog.NOMBRE,subpro.NOMBRE ";
		} else if (criteria.getTipoReporte().equals(
				ReporteTotalComunaService.TIPO_REPORTE_2)) {
			query += " GROUP BY comuna.NOMBRE, area.nombre, prog.NOMBRE, subpro.NOMBRE ";
			query += " ORDER BY comuna.NOMBRE, area.nombre, prog.NOMBRE, subpro.NOMBRE ";
		}

		System.out.println(query);
		System.out.println(namedParameters.toString());

		ArrayList<ReportComunaVO> lbeneotor = new ArrayList<ReportComunaVO>();

		long totalvalor = 0;
		long totalben = 0; // total beneficios
		long totalber = 0; // total beneficiarios
		int firstRow = 0;

		List<Map<String, Object>> rows = getNamedParameterJdbcTemplate()
				.queryForList(query.toString(), namedParameters);
		for (Map<String, Object> row : rows) {

			BigDecimal contBeneficio = (BigDecimal) row.get("contbeneficio");
			BigDecimal contBeneficiario = (BigDecimal) row
					.get("contbeneficiario");
			BigDecimal valor = (BigDecimal) row.get("valor");

			String nombrePrograma = (String) row.get("nompro");
			String nombreSubprograma = (String) row.get("nomsubpro");
			String nombreComuna = (String) row.get("nomcomuna");
			if (nombreComuna == null || nombreComuna.isEmpty()) {
				nombreComuna = SIN_COMUNA;
			}
			String nombreSecretaria = (String) row.get("nombre_secretaria");

			ReportComunaVO vo = new ReportComunaVO();
			vo.setNombrePrograma(nombrePrograma);
			vo.setNombreSubprograma(nombreSubprograma);
			vo.setNombreComuna(nombreComuna);

			if (contBeneficio != null) {
				vo.setContben(contBeneficio.longValueExact());
			}
			if (contBeneficiario != null) {
				vo.setContber(contBeneficiario.longValueExact());
			}
			if (valor != null) {
				vo.setValor(valor.longValueExact());
			}

			vo.setNombreSecretaria(nombreSecretaria);

			if (valor != null) {
				totalvalor += valor.longValueExact();
			}
			if (contBeneficio != null) {
				totalben += contBeneficio.longValueExact();
			}
			// totalber += rst.getLong("contbeneficiario");

			if (firstRow == 0) {
				BigDecimal totalBeneficiarios = (BigDecimal) row
						.get("TOTAL_BENEFICIARIOS");
				if (totalBeneficiarios != null) {
					totalber = totalBeneficiarios.longValueExact();
				}
				firstRow++;
			}
			lbeneotor.add(vo);
		}
		if (lbeneotor.size() > 0) {
			ReportComunaVO vo = new ReportComunaVO();
			vo.setNombreComuna("VALOR TOTAL");
			vo.setContben(totalben);
			vo.setContber(totalber);
			vo.setValor(totalvalor);
			lbeneotor.add(vo);
		}

		return lbeneotor;
	}

	public ReporteTotalComunaVO findFilters(ReporteTotalComunaVO objRtcVO) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		String strQuery = null;

		// consultar nombre de la zona.
		if (objRtcVO.getCodeZona() != null) {
			strQuery = "SELECT NOMBRE FROM PAOYER.TCS_VALOR_FLEXIBLE "
					+ " WHERE CODIGO = :CODIGO ";

			namedParameters.put("CODIGO", objRtcVO.getCodeZona());
			try {
				String nombreZona = getNamedParameterJdbcTemplate()
						.queryForObject(strQuery, namedParameters, String.class);
				objRtcVO.setNombreZona(nombreZona);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: throw?
			}
		}

		// consultar nombre de la comuna.
		if (objRtcVO.getCodeComuna() != null) {
			strQuery = "SELECT NOMBRE FROM PAOYER.TCS_VALOR_FLEXIBLE "
					+ " WHERE CODIGO = :CODIGO ";

			namedParameters.put("CODIGO", objRtcVO.getCodeComuna());
			try {
				String nombreComuna = getNamedParameterJdbcTemplate()
						.queryForObject(strQuery, namedParameters, String.class);
				objRtcVO.setNombreComuna(nombreComuna);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: throw?
			}

		}
		if (objRtcVO.getCodePrograma() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_PROGRAMA "
					+ " WHERE CODIGO = :CODIGO ";

			namedParameters.clear();
			namedParameters.put("CODIGO", objRtcVO.getCodePrograma());
			try {
				String nombrePrograma = getNamedParameterJdbcTemplate()
						.queryForObject(strQuery, namedParameters, String.class);
				objRtcVO.setNombrePrograma(nombrePrograma);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: throw?
			}
		}

		if (objRtcVO.getCodeSubprograma() != null) {
			strQuery = "SELECT NOMBRE FROM TCS_BEN_SUBPROGRAMA "
					+ " WHERE CODIGO = :CODIGO ";

			namedParameters.clear();
			namedParameters.put("CODIGO", objRtcVO.getCodeSubprograma());
			try {
				String nombreSubprograma = getNamedParameterJdbcTemplate()
						.queryForObject(strQuery, namedParameters, String.class);
				objRtcVO.setNombreSubprograma(nombreSubprograma);
			} catch (IncorrectResultSizeDataAccessException e) {
				// TODO: throw?
			}
		}
		return objRtcVO;
	}

	/**
	 * HACK.. Metodo que retorna un Object[] que contiene los strings y
	 * parametros a concatenar en la consulta del metodo
	 * findBeneficiosBeneficiariosComuna.
	 * 
	 * @param filtros
	 * @return Array de Object, primer posicion String a concatenar (ej:
	 *         "AND ben.FECHA_NACIMIENTO >= TO_DATE('01/01/2000')", segunda
	 *         posicion List parametros query, tercera posicion string a
	 *         concatenar subquery, cuarta posicion List parametros subquery.
	 */
	private Object[] buildQueryFiltros(Collection<CondicionConsultaVO> filtros) {
		Map<String, Object> namedParameters = new HashMap<String, Object>();

		List<CondicionConsultaVO> lFiltros = (List<CondicionConsultaVO>) filtros;
		CondicionConsultaVO filtro = null;
		Object[] ret = new Object[4];
		StringBuffer query = new StringBuffer();
		StringBuffer sbQuery = new StringBuffer();

		if ((Object) filtros != null) {
			for (int i = 0; i < lFiltros.size(); i++) {
				filtro = (CondicionConsultaVO) lFiltros.get(i);
				String operador = filtro.getCodFiltro();
				String tipoDato = filtro.getTipoCampo();

				query.append(" AND ");
				sbQuery.append(" AND ");
				if (tipoDato.equals(FiltrosReportesUtil.TIPO_NUMERICO)) {

					if (filtro.getCodcampo().equals(FiltrosReportesUtil.EDAD)) {
						String fechaIni = "";
						String fechaFin = "";
						int edad1 = Integer.parseInt(filtro.getValor());
						fechaIni = FiltrosReportesUtil
								.convertAgeToDateOfBirth(edad1);
						if (operador.equals(FiltrosReportesUtil.ENTRE)) {
							int edad2 = Integer.parseInt(filtro.getValor2());
							fechaFin = FiltrosReportesUtil
									.convertAgeToDateOfBirth(edad2);
						}
						if (operador.equals(FiltrosReportesUtil.ENTRE)) {
							// se invierte el orden de las fechas.
							query.append("ben."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + FiltrosReportesUtil.ENTRE
									+ " TO_DATE(:fechaFin_" + i
									+ ", 'dd/mm/rrrr') AND TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");

							namedParameters.put("fechaFin_" + i, fechaFin);
							namedParameters.put("fechaIni_" + i, fechaIni);

							sbQuery.append("ben_."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + FiltrosReportesUtil.ENTRE
									+ " TO_DATE(:fechaFin_" + i
									+ ", 'dd/mm/rrrr') AND TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");

						} else {// se invierten los operadores debido a que
								// ahora se comparan fechas y no edades.
							if (operador.equals(FiltrosReportesUtil.MAYOR)) {
								operador = FiltrosReportesUtil.MENOR;
							} else if (operador
									.equals(FiltrosReportesUtil.MAYOR_IGUAL)) {
								operador = FiltrosReportesUtil.MENOR_IGUAL;
							} else if (operador
									.equals(FiltrosReportesUtil.MENOR)) {
								operador = FiltrosReportesUtil.MAYOR;
							} else if (operador
									.equals(FiltrosReportesUtil.MENOR_IGUAL)) {
								operador = FiltrosReportesUtil.MAYOR_IGUAL;
							}
							query.append("ben."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + operador + " TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");

							sbQuery.append("ben_."
									+ FiltrosReportesUtil.FECHA_NACIMIENTO
									+ " " + operador + " TO_DATE(:fechaIni_"
									+ i + ", 'dd/mm/rrrr')");
							namedParameters.put("fechaIni_" + i, fechaIni);

						}
						continue;
					}

					if (operador.equals(FiltrosReportesUtil.IGUAL)
							|| operador.equals(FiltrosReportesUtil.DIFERENTE)
							|| operador.equals(FiltrosReportesUtil.MENOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MAYOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MENOR)
							|| operador.equals(FiltrosReportesUtil.MAYOR)) {
						query.append(" ben." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						sbQuery.append(" ben_." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i + " ");

					} else {// BETWEEN
						query.append("ben." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						namedParameters.put("filtro.valor2_" + i,
								filtro.getValor2());
						sbQuery.append("ben_." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");

					}

				} else if (tipoDato.equals(FiltrosReportesUtil.TIPO_FECHA)) {

					if (operador.equals(FiltrosReportesUtil.ENTRE)
							|| operador.equals(FiltrosReportesUtil.IGUAL)) {
						query.append("ben." + filtro.getCodcampo() + " "
								+ operador + " TO_DATE(:filtro.valor_" + i
								+ ", 'dd/mm/rrrr') AND TO_DATE(:filtro.valor2_"
								+ i + ",'dd/mm/rrrr')");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						namedParameters.put("filtro.valor2_" + i,
								filtro.getValor2());
						sbQuery.append("ben_." + filtro.getCodcampo() + " "
								+ operador + " TO_DATE(:filtro.valor_" + i
								+ ", 'dd/mm/rrrr') AND TO_DATE(:filtro.valor2_"
								+ i + ", 'dd/mm/rrrr')");

					} else {
						query.append("ben." + filtro.getCodcampo() + " "
								+ operador + " TO_DATE(:filtro.valor_" + i
								+ ", 'dd/mm/rrrr')");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						sbQuery.append("ben_." + filtro.getCodcampo() + " "
								+ operador + " TO_DATE(:filtro.valor_" + i
								+ ", 'dd/mm/rrrr')");

					}

				} else if (tipoDato
						.equals(FiltrosReportesUtil.TIPO_ALFANUMERICO)
						|| tipoDato.equals("L")) {
					if (operador.equals(FiltrosReportesUtil.IGUAL)
							|| operador.equals(FiltrosReportesUtil.DIFERENTE)) {
						query.append(" UPPER(ben." + filtro.getCodcampo()
								+ " ) " + operador + " :filtro.valor_" + i
								+ " ");
						namedParameters.put("filtro.valor_" + i, filtro
								.getValor().toUpperCase());
						sbQuery.append(" UPPER(ben_." + filtro.getCodcampo()
								+ " ) " + operador + " :filtro.valor_" + i
								+ " ");

					} else if (operador.equals(FiltrosReportesUtil.PARECIDO)) {
						query.append(" UPPER(ben." + filtro.getCodcampo()
								+ " ) " + FiltrosReportesUtil.PARECIDO
								+ " :filtro.valor_" + i + " ");
						namedParameters.put("filtro.valor_" + i, "%"
								+ filtro.getValor().toUpperCase() + "%");
						sbQuery.append(" UPPER(ben_." + filtro.getCodcampo()
								+ " ) " + FiltrosReportesUtil.PARECIDO
								+ " :filtro.valor_" + i + " ");

					}
				} else {
					if (operador.equals(FiltrosReportesUtil.IGUAL)
							|| operador.equals(FiltrosReportesUtil.DIFERENTE)
							|| operador.equals(FiltrosReportesUtil.MENOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MAYOR_IGUAL)
							|| operador.equals(FiltrosReportesUtil.MENOR)
							|| operador.equals(FiltrosReportesUtil.MAYOR)) {
						query.append(" ben." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						sbQuery.append(" ben_." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i + " ");

					} else { // BETWEEN
						query.append("ben." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");
						namedParameters.put("filtro.valor_" + i,
								filtro.getValor());
						namedParameters.put("filtro.valor2_" + i,
								filtro.getValor2());
						sbQuery.append("ben_." + filtro.getCodcampo() + " "
								+ operador + " :filtro.valor_" + i
								+ " AND :filtro.valor2_" + i + " ");

					}

				}

			}
		}
		ret[0] = query.toString();
		ret[1] = namedParameters;
		ret[2] = sbQuery.toString();
		ret[3] = namedParameters;
		// System.out.println("retorno: " + retorno);
		return ret;
	}

}
